//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals.World;
using Action = Styx.TreeSharp.Action;

#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;


namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    public class ShadoPanMonastery : Dungeon
    {
        #region Overrides of Dungeon

        private readonly WoWPoint _azureSerpentFollowerLoc = new WoWPoint(3736.51, 2670.499, 768.0417);
        private readonly WoWPoint _azureSerpentTankLoc = new WoWPoint(3727.666, 2688.185, 768.0416);

        public override uint DungeonId
        {
            get { return 466; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(3643.886, 2544.806, 769.9496); }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret.ToUnit();
                    if (unit != null)
                    {
                        if (unit.Entry == GuCloudstrikeId && unit.HasAura("Charging Soul"))
                            return true;
                        if (unit.Entry == ShadoPanNoviceId && unit.HasAura("Parry Stance") && unit.IsFacing(Me))
                            return true;
                        if (unit.Entry == MasterSnowdriftId && !Me.IsHealer())
                        {
                            if (unit.CastingSpellId == ParryStanceId || unit.HasAura(ParryStanceId))
                                return true;
                            if ((unit.HasAura(PursuitId) && unit.CurrentTargetGuid == Me.Guid || unit.CastingSpellId == TornadoKickId) && Me.IsMelee())
                                return true;
                        }
                        if (unit.Entry == LesserVolatileEnergyId && ScriptHelpers.GroupMembers.Where(m => m.IsRange && m.IsDps).OrderByDescending(r => r.MaxHealth).Select(r => r.Guid).FirstOrDefault() != Me.Guid)
                            return true;

                        if (unit.Entry == FlyingSnowId && unit.HasAura("Whirling Steel") && Me.IsDps() && Me.IsMelee())
                            return true;

                        if (unit.Entry == AzureSerpentId && unit.HasAura("Lightning Shielded") && !Me.IsHealer())
                            return true;

                        if (unit.Entry == GrippingHatred && StyxWoW.Me.IsTank())
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            var tank = ScriptHelpers.Tank;
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == MasterSnowCopyId)
                        outgoingunits.Add(unit);
                    // kill any hatefull essences that are withing range of a slain shado Pan defender which is protecting the hostile units I'm in combat with atm
                    if (unit.Entry == HatefulEssenceId)
                    {
                        if (!ScriptHelpers.GroupMembers.Any(g => g.Player != null && g.Player.Combat))
                            continue;
                        // force followers to attack
                        if (tank != null && tank.CurrentTargetGuid == unit.Guid)
                        {
                            outgoingunits.Add(unit);
                            continue;
                        }
                        var immuneUnit = Targeting.Instance.TargetList.FirstOrDefault(t => t.HasAura("Apparitions"));
                        if (immuneUnit != null)
                        {
                            var defender = ObjectManager.GetObjectByGuid<WoWUnit>(immuneUnit.Auras["Apparitions"].CreatorGuid);
                            if (defender != null && defender.Location.Distance(unit.Location) <= 15)
                                outgoingunits.Add(unit);
                        }
                    }

                    if (unit.Entry == GrippingHatred && Me.IsDps() &&
                        (Me.IsRange() || Me.IsMelee() && ScriptHelpers.GetGroupMembersByRole(ScriptHelpers.GroupRoleFlags.Ranged).Count(p => p.IsAlive) < 3))
                        outgoingunits.Add(unit);
                    if (unit.Entry == LesserVolatileEnergyId && GroupMember.GroupMembers.Where(m => m.IsDps).OrderByDescending(r => r.MaxHealth).Select(r => r.Guid).FirstOrDefault() == Me.Guid)
                    {
                        outgoingunits.Add(unit);
                    }
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (StyxWoW.Me.IsDps())
                    {
                        if (unit.Entry == ShadoPanWardenId && unit.CastingSpellId == FocusEnergy)
                            priority.Score += 500;

                        if (unit.Entry == GrippingHatred)
                            priority.Score += 50000;
                    }

                    if (unit.Entry == HatefulEssenceId && Me.IsDps())
                        priority.Score += 50000;
                    if (unit.Entry == LesserVolatileEnergyId &&
                         GroupMember.GroupMembers.Where(r => r.IsRange && r.IsDps).OrderByDescending(r => r.MaxHealth).Select(r => r.Guid).FirstOrDefault() == Me.Guid)
                    {
                        priority.Score += 50000;
                    }
                }
            }
        }



        public override MoveResult MoveTo(WoWPoint location)
        {
            var myLoc = Me.Location;
            if (!Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry == AzureSerpentId && location == Targeting.Instance.FirstUnit.Location)
            {
                if (Me.IsTank() && myLoc.DistanceSqr(_azureSerpentTankLoc) > 4 * 4)
                    return Navigator.MoveTo(_azureSerpentTankLoc);
                if (!Me.IsTank() && myLoc.DistanceSqr(_azureSerpentFollowerLoc) > 4 * 4)
                    return Navigator.MoveTo(_azureSerpentFollowerLoc);
                return MoveResult.Moved;
            }

            return base.MoveTo(location);
        }

        #endregion

        private const uint ShadoPanWardenId = 59751;
        private const uint GuCloudstrikeId = 56747;
        private const uint AzureSerpentId = 56754;
        private const uint FlyingSnowId = 56473;
        private const uint BallOfFireId = 59225;
        private const uint ShadoPanNoviceId = 56395;
        private const uint FireFlowerId = 56646;
        private const uint MasterSnowDriftsBallOfFireId = 56640;
        private const uint MasterSnowCopyId = 56713;
        private const int ParryStanceId = 106454;
        private const int FistsOfFuryId = 106853;
        private const int PursuitId = 106880;
        private const int TornadoKickId = 106434;
        private const int FocusEnergy = 115009;
        private const uint MasterSnowdriftId = 56541;
        private const int SmokeBladesId = 106826;
        private const uint ShaOfViolence = 56719;
        private const uint LesserVolatileEnergyId = 66652;
        private const uint HatefulEssenceId = 58812;
        private const uint GrippingHatred = 59804;
        private const uint ResidualHatredId = 58803;
        private const uint SlainShadoPanDefender = 58794;
        private readonly uint[] _taranZhusPersonalStashIs = new uint[] { 213888, 213889 };

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        #region Root

        [EncounterHandler(0, "Root")]
        public Composite RootEncounter()
        {
            AddAvoidObject(ctx => !Me.IsTank(), 8, u => u.Entry == FlyingSnowId && ((WoWUnit)u).HasAura("Whirling Steel"));
            AddAvoidObject(ctx => true, 5, FireFlowerId);

            return new PrioritySelector(
                // from shado-pan novices
                ScriptHelpers.CreateDispellParty("Black Cleave", ScriptHelpers.PartyDispellType.Magic),
                // deal with Slain Shado-Pan Defender.
                new Decorator(
                    ctx => Me.Combat && Me.IsDps(),
                    new PrioritySelector(
                        ctx =>
                        {
                            var immuneUnit = Targeting.Instance.TargetList.FirstOrDefault(t => t.HasAura("Apparitions"));
                            return immuneUnit != null ? ObjectManager.GetObjectByGuid<WoWUnit>(immuneUnit.Auras["Apparitions"].CreatorGuid) : null;
                        },
                        new Decorator<WoWUnit>(
                            defender =>
                            Me.PartyMembers.All(p => p.ChannelObject != defender) &&
                            !ScriptHelpers.GetUnfriendlyNpsAtLocation(() => defender.Location, 12, u => u.Entry == HatefulEssenceId).Any(),
                            new PrioritySelector(
                                new Decorator<WoWUnit>(defender => defender.DistanceSqr > 4 * 4, new Helpers.Action<WoWUnit>(defender => Navigator.MoveTo(defender.Location))),
                                new Decorator<WoWUnit>(
                                    defender => defender.DistanceSqr <= 4 * 4 && !Me.IsChanneling, new Helpers.Action<WoWUnit>(defender => defender.Interact())))))));
        }

        [EncounterHandler(64387, "Master Snowdrift", Mode = CallBehaviorMode.Proximity, BossRange = 30)]
        [EncounterHandler(62236, "Ban Bearheart", Mode = CallBehaviorMode.Proximity, BossRange = 30)]
        [EncounterHandler(56541, "Master Snowdrift", Mode = CallBehaviorMode.Proximity, BossRange = 30)]
        [EncounterHandler(56884, "Corrupted Taran Zhu", Mode = CallBehaviorMode.Proximity, BossRange = 30)]
        public Composite QuestHandler()
        {
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.Available,
                    ScriptHelpers.CreatePickupQuest(() => unit)),
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.TurnIn,
                    ScriptHelpers.CreateTurninQuest(() => unit)));
        }

        #endregion

        #region Gu Cloudstrike

        [EncounterHandler(56747, "Gu Cloudstrike", Mode = CallBehaviorMode.Proximity, BossRange = 100)]
        public Composite GuCloudstrikeEncounter()
        {
            WoWUnit boss = null;
            const uint staticFieldStalkerId = 56803;
            const int invokeLightningSpellId = 106984;
            const int staticFieldSpellId = 106923;

            var swDoorSideLoc = new WoWPoint(3681.217, 2634.085, 771.2478);
            var neDoorSideLoc = new WoWPoint(3686.494, 2624.75, 771.2479);

            var insideDoorLoc = new WoWPoint(3688.689, 2632.12, 771.2478);

            AddAvoidObject(ctx => true, 10, staticFieldStalkerId);
            AddAvoidLocation(ctx => true, 10, m => ((WoWMissile)m).ImpactPosition, () => WoWMissile.InFlightMissiles.Where(m => m.SpellId == staticFieldSpellId));
            AddAvoidObject(
                ctx => true, 5, u => boss != null && boss.IsValid && boss.CastingSpellId == invokeLightningSpellId && u.Guid == boss.CurrentTargetGuid && u.Guid != Me.Guid);

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(
                    ctx => !boss.Combat && !Me.Combat,
                    new PrioritySelector(
                        new Decorator(
                            ctx =>
                            {
                                var tank = ScriptHelpers.Tank;
                                return tank != null && !tank.IsMe && tank.Location.IsPointLeftOfLine(swDoorSideLoc, neDoorSideLoc) &&
                                       !Me.Location.IsPointLeftOfLine(swDoorSideLoc, neDoorSideLoc);
                            },
                            new Action(ctx => Navigator.MoveTo(insideDoorLoc))),
                // if not all the followers are inside the door then wait..
                        new Decorator(
                            ctx =>
                            Me.IsTank() && Me.Location.IsPointLeftOfLine(swDoorSideLoc, neDoorSideLoc) &&
                            !Me.PartyMembers.All(p => p.Location.IsPointLeftOfLine(swDoorSideLoc, neDoorSideLoc)),
                            new PrioritySelector(
                                new ActionSetActivity("Waiting on followers to get inside door."),
                                new Decorator(ctx => Me.IsMoving, new Action(ctx => WoWMovement.MoveStop())),
                                new ActionAlwaysSucceed())))),
                // stack up for aoe heals and to deal with the debuff in phase 2.
                new Decorator(
                    ctx => boss.HasAura("Charging Soul"),
                    new PrioritySelector(
                        new Decorator(ctx => Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentTankLoc) > 10 * 10, new Action(ctx => Navigator.MoveTo(_azureSerpentTankLoc))),
                        new Decorator(
                            ctx => !Me.IsTank() && Me.Location.DistanceSqr(_azureSerpentFollowerLoc) > 10 * 10, new Action(ctx => Navigator.MoveTo(_azureSerpentFollowerLoc))))));
        }

        #endregion

        #region Master Snowdrift

        // this is the wall before Master Snowdrift that blocks that path until mini-bosses have been defeated..
        // tanks needs to wait for NPCs to spawn and this is what this behavior does.
        [ObjectHandler(212908, "Shadowpan Hideout - Weaponmaster Gauntlet - Collision")]
        public Composite ShadowpanHideoutWeaponmasterGauntletCollisionHandler()
        {
            var roomCenterLoc = new WoWPoint(3658.823, 3015.969, 804.6611);
            return new Decorator<WoWGameObject>(
                wall => wall.State == WoWGameObjectState.Ready && Me.IsTank() && Targeting.Instance.IsEmpty(),
                new PrioritySelector(
                    new Decorator(ctx => Me.Location.Distance(roomCenterLoc) > 10, new Action(ctx => Navigator.MoveTo(roomCenterLoc))), new ActionAlwaysSucceed()));
        }

        [EncounterHandler(56541, "Master Snowdrift", Mode = CallBehaviorMode.Proximity, BossRange = 100)]
        public Composite MasterSnowdriftEncounter()
        {
            var roomCenter = new WoWPoint(3713.436, 3091.417, 817.3193);
            var swDoorOpeningSideLoc = new WoWPoint(3682.04, 3067.78, 816.4683);
            var neDoorOpeningSideLoc = new WoWPoint(3701.133, 3054.188, 816.3525);
            var insideDoorLoc = new WoWPoint(3695.023, 3065.702, 816.201);

            AddAvoidObject(
                ctx => true,
                4,
                u => u.Entry == MasterSnowDriftsBallOfFireId || u.Entry == BallOfFireId,
                u =>
                {
                    var start = u.Location;
                    return Me.Location.GetNearestPointOnSegment(start, start.RayCast(WoWMathHelper.NormalizeRadian(u.Rotation), 20));
                });

            AddAvoidObject(ctx => true, () => roomCenter, 17, 10, u => u.Entry == MasterSnowdriftId && ((WoWUnit)u).CastingSpellId == TornadoKickId);
            AddAvoidObject(
                ctx => true, () => roomCenter, 17, 12, u => u.Entry == MasterSnowdriftId && ((WoWUnit)u).HasAura(PursuitId) && ((WoWUnit)u).CurrentTargetGuid == Me.Guid);

            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(
                    ctx => boss.IsFriendly && ScriptHelpers.IsBossAlive("Master Snowdrift"), new Action(ctx => ScriptHelpers.MarkBossAsDead("Master Snowdrift", "He got his ass whooped"))),
                // pickup quest.
                new Decorator<WoWUnit>(unit => unit.QuestGiverStatus == QuestGiverStatus.Available, ScriptHelpers.CreatePickupQuest(() => boss)),
                // move inside door before encounter starts
                new Decorator(
                    ctx => boss.IsHostile && !boss.Combat && !Me.Combat,
                    new PrioritySelector(
                        new Decorator(
                            ctx =>
                            {
                                var tank = ScriptHelpers.Tank;
                                return tank != null && !tank.IsMe && tank.Location.IsPointLeftOfLine(swDoorOpeningSideLoc, neDoorOpeningSideLoc) &&
                                       !Me.Location.IsPointLeftOfLine(swDoorOpeningSideLoc, neDoorOpeningSideLoc);
                            },
                            new Action(ctx => Navigator.MoveTo(insideDoorLoc))),
                // if not all the followers are inside the door then wait..
                        new Decorator(
                            ctx =>
                            Me.IsTank() && Me.Location.IsPointLeftOfLine(swDoorOpeningSideLoc, neDoorOpeningSideLoc) &&
                            !Me.PartyMembers.All(p => p.Location.IsPointLeftOfLine(swDoorOpeningSideLoc, neDoorOpeningSideLoc)),
                            new PrioritySelector(
                                new ActionSetActivity("Waiting on followers to get inside door."),
                                new Decorator(ctx => Me.IsMoving, new Action(ctx => WoWMovement.MoveStop())),
                                new ActionAlwaysSucceed())))),
                new Decorator<WoWUnit>(
                    unit => unit.Combat,
                    new PrioritySelector(
                        ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                            ctx => boss.CastingSpellId == FistsOfFuryId && boss.Distance < 8, () => boss, new ScriptHelpers.AngleSpan(0, 180)),
                        new Decorator(
                            ctx => (boss.CastingSpellId == ParryStanceId || boss.HasAura(ParryStanceId)) && !Me.IsHealer() && Me.CurrentTargetGuid > 0,
                            new Action(ctx => Me.ClearTarget())),
                // don't go anywhere if target list is empty.
                        new Decorator(ctx => Me.IsTank() && Targeting.Instance.IsEmpty(), new ActionAlwaysSucceed()))));
        }


        [ObjectHandler(214518, "Snowdrift's Possessions")]
        [ObjectHandler(214519, "Snowdrift's Possessions")]
        public Composite SnowdriftsPossessionsHandler()
        {
            WoWGameObject chest = null;
            return new PrioritySelector(ctx => chest = ctx as WoWGameObject, ScriptHelpers.CreateLootChest(() => chest));
        }

        #endregion

        #region Sha of Violence

        [EncounterHandler(56764, "Consuming Sha")]
        public Composite ConsumingShaEncounter()
        {
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                ScriptHelpers.CreateDispellParty("Consumption", ScriptHelpers.PartyDispellType.Magic),
                ScriptHelpers.CreateDispellEnemy("Consuming Bite", ScriptHelpers.EnemyDispellType.Enrage, () => unit));
        }

        [EncounterHandler(56763, "Regenerating Sha")]
        public Composite RegeneratingShaEncounter()
        {
            const int RegenerateSpellId = 106920;

            WoWUnit unit = null;
            return new PrioritySelector(ctx => unit = ctx as WoWUnit, ScriptHelpers.CreateInterruptCast(() => unit, RegenerateSpellId));
        }

        [EncounterHandler(56765, "Destroying Sha")]
        public Composite DestroyingShaEncounter()
        {
            WoWUnit unit = null;
            const int shadowsOfDestruction = 106942;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                ScriptHelpers.CreateInterruptCast(() => unit, shadowsOfDestruction),
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(ctx => unit.CastingSpellId == shadowsOfDestruction && !Me.IsTank() && unit.DistanceSqr <= 15 * 15, () => unit, new ScriptHelpers.AngleSpan(0, 160)),
                new Decorator(ctx => Me.IsTank() && Targeting.Instance.TargetList.All(t => t.CurrentTargetGuid == Me.Guid),
                    ScriptHelpers.CreateTankFaceAwayGroupUnit(() => unit, 15)));
        }

        const uint ShaOfViolenceId = 56719;

        [EncounterHandler(56719, "Sha of Violence")]
        public Composite ShaofViolenceEncounter()
        {
            var roomCenter = new WoWPoint(3996.92, 2905.266, 770.3101);
            const int shaSpikeId = 106877;
            var shaSpikeTimer = new WaitTimer(TimeSpan.FromSeconds(5));
            var shaSpikeLoc = WoWPoint.Zero;

            AddAvoidObject(ctx => Me.IsRange(), () => roomCenter, 25, 12, u => u.Entry == ShaOfViolence && ((WoWUnit)u).CastingSpellId == SmokeBladesId);

            AddAvoidObject(
                ctx => true,
                5,
                o => o.Entry == ShaOfViolenceId && o.ToUnit().CastingSpellId == shaSpikeId && o.ToUnit().CurrentTargetGuid != 0,
                o =>
                {
                    if (shaSpikeTimer.IsFinished)
                    {
                        shaSpikeLoc = o.ToUnit().CurrentTarget.Location;
                        shaSpikeTimer.Reset();
                    }
                    return shaSpikeLoc;
                });

            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateDispellEnemy("Enrage", ScriptHelpers.EnemyDispellType.Enrage, () => boss),
                ScriptHelpers.CreateDispellParty("Disorienting Smash", ScriptHelpers.PartyDispellType.Magic));
        }

        #endregion

        #region Shortcuts

        [LocationHandler(3863.545, 2834.767, 753.7231, 20, "Shortcut at 1st bridge towards last boss")]
        public Composite Shortcut1()
        {
            var jump1StartLoc = new WoWPoint(3863.545f, 2834.767f, 753.7231f);
            var jump1EndLoc = new WoWPoint(3860.631f, 2819.449f, 735.3653f);

            WoWPlayer tank = null;
            return new PrioritySelector(
                new Decorator(ctx => Targeting.Instance.IsEmpty() && Me.Location.DistanceSqr(jump1StartLoc) < 20 * 20 && PackAtEndOfFirstBridgeToLastBoss && Me.Z > 740 &&
                    (Me.IsTank() || (tank = ScriptHelpers.Tank) != null && (tank.Distance >= 60 || tank.Z < 740)),
                    new PrioritySelector(
                        new Decorator(ctx => Me.Location.DistanceSqr(jump1StartLoc) > 5, new Action(ctx => Navigator.MoveTo(jump1StartLoc))),
                        new Decorator(ctx => Me.Location.DistanceSqr(jump1StartLoc) <= 5, new Action(ctx => Navigator.PlayerMover.MoveTowards(jump1EndLoc)))
                        )
                    )
                );
        }


        [LocationHandler(3848.596, 2648.597, 752.523, 40, "Shortcut at 2st bridge towards last boss")]
        public Composite Shortcut2()
        {
            // I believe this pack should be killed because there's a chance of aggroing during boss encounter thus if I'm tank just pull group but as DPS avoid it if tank doesn't pull it

            WoWPlayer tank = null;
            var jump2StartLoc = new WoWPoint(3850.932f, 2677.368f, 749.0594f);
            var jump2EndLoc = new WoWPoint(3878.418, 2657.065, 757.0981);
            AddAvoidLocation(ctx => !Me.IsTank() && Targeting.Instance.IsEmpty() && PackAtEndOfSecondBridgeToLastBoss, 25, o => _skipPack2Loc);
            return new PrioritySelector(
                    new Decorator(ctx => !Me.IsTank() && Targeting.Instance.IsEmpty() && Me.Location.DistanceSqr(jump2StartLoc) < 12 * 12 && PackAtEndOfSecondBridgeToLastBoss && Me.Y > 2676 && Me.X < 3848,
                    new Sequence(
                        ScriptHelpers.CreateMoveToContinue(() => jump2StartLoc),
                        new Action(ctx =>
                                       {
                                           Navigator.PlayerMover.MoveTowards(jump2EndLoc);
                                           return Me.Location.DistanceSqr(jump2EndLoc) <= 4 ? RunStatus.Success : RunStatus.Running;
                                       }),
                        new WaitContinue(3, ctx => Me.Y > 2673, new ActionAlwaysSucceed())
                        )
                    )
                );
        }

        readonly WoWPoint _skipPack1Loc = new WoWPoint(3844.406, 2780.82, 745.8341);
        bool PackAtEndOfFirstBridgeToLastBoss
        {
            get { return ScriptHelpers.GetUnfriendlyNpsAtLocation(() => _skipPack1Loc, 20).Any(); }
        }

        readonly WoWPoint _skipPack2Loc = new WoWPoint(3849.431, 2645.121, 752.9745);
        bool PackAtEndOfSecondBridgeToLastBoss
        {
            get { return ScriptHelpers.GetUnfriendlyNpsAtLocation(() => _skipPack2Loc, 20).Any(); }
        }

        #endregion

        #region Corrupted Taran Zhu


        [EncounterHandler(58810, "Fragment of Hatred")]
        public Composite FragmentOfHatredEncounter()
        {
            WoWUnit unit = null;
            const int volleyOfHatred = 112911;
            return new PrioritySelector(ctx => unit = ctx as WoWUnit, ScriptHelpers.CreateInterruptCast(() => unit, volleyOfHatred));
        }

        [EncounterHandler(58803, "Residual Hatred")]
        public Composite ResidualHatredEncounter()
        {
            AddAvoidObject(
                ctx => Me.IsRange() || Me.IsMelee() && !Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry != ResidualHatredId,
                () => ScriptHelpers.Tank.Location,
                30,
                17,
                u => u.Entry == ResidualHatredId && ((WoWUnit)u).HasAura("Ring of Malice") && u.Distance >= 9);

            return new PrioritySelector();
        }

        const uint TaranZhu = 56884;

        [EncounterHandler(56884, "Corrupted Taran Zhu", Mode = CallBehaviorMode.Proximity)]
        public Composite CorruptedTaranZhuEncounter()
        {
            var tankLoc = new WoWPoint(3861.51, 2615.981, 754.5428);
            WoWUnit boss = null;
            int hatred = 0;
            const int risingHateId = 107356;

            AddAvoidObject(
                ctx => Me.IsRange() || Me.IsDps() && Me.IsMelee() && !Targeting.Instance.IsEmpty() && Targeting.Instance.FirstUnit.Entry != TaranZhu,
                () => tankLoc,
                30,
                17,
                u => u.Entry == TaranZhu && ((WoWUnit)u).HasAura("Ring of Malice") && u.Distance >= 9);
            // run away from Gripping Hatred if not killing it.
            AddAvoidObject(
                ctx => true,
                () => tankLoc,
                30,
                9,
                u => u.Entry == GrippingHatred && u.ToUnit().HasAura("Pool of Shadows") && (Targeting.Instance.FirstUnit != u || Me.IsRange()));

            return new PrioritySelector(
                ctx =>
                {
                    hatred = Lua.GetReturnVal<int>("return UnitPower('player',10)", 0);
                    return boss = ctx as WoWUnit;
                },
                new Decorator(
                    ctx => boss.IsFriendly,
                    new PrioritySelector(
                        new Decorator(
                            ctx => ScriptHelpers.IsBossAlive("Corrupted Taran Zhu"),
                            new Action(ctx => ScriptHelpers.MarkBossAsDead("Corrupted Taran Zhu", "He got his ass whooped"))))),
                new Decorator(
                    ctx => boss.Combat,
                    new PrioritySelector(
                        ScriptHelpers.CreateInterruptCast(() => boss, risingHateId),
                        new Decorator(ctx => hatred >= 45, new Action(ctx => Lua.DoString("ExtraActionButton1:Click()"))),
                        new Decorator(ctx => !boss.HasAura("Ring of Malice"), ScriptHelpers.CreateTankUnitAtLocation(() => tankLoc, 5)))));
        }


        [ObjectHandler(213888, "Taran Zhu's Personal Stash")]
        [ObjectHandler(213889, "Taran Zhu's Personal Stash")]
        public Composite TaranZhusPersonalStashHandler()
        {
            WoWGameObject chest = null;
            return new PrioritySelector(ctx => chest = ctx as WoWGameObject, ScriptHelpers.CreateLootChest(() => chest));
        }

        #endregion

    }
    
    public class ShadoPanMonasteryHeroic : ShadoPanMonastery
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 470; }
        }
        #endregion
    }

}

